﻿using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenCvSharp.Extensions;
using YTSK.Model;

namespace YTSK.Common
{
    public class HandleImg
    {
        /// <summary>
        /// 屏幕截图
        /// </summary>
        /// <param name="x">截图左距离</param>
        /// <param name="y">截图上边距离</param>
        /// <param name="width">宽度</param>
        /// <param name="height">高度</param>
        /// <returns></returns>
        public static Bitmap GetScreenImg(int x, int y, int width, int height)
        {
            var bmp = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.CopyFromScreen(x, y, 0, 0, new System.Drawing.Size(width, height));
            }

            return bmp;
        }


        /// <summary>
        /// 图像灰度化
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static Bitmap ToGray(Bitmap bmp)
        {
            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    //获取该点的像素的RGB的颜色
                    Color color = bmp.GetPixel(i, j);
                    //利用公式计算灰度值
                    int gray = (int) (color.R * 0.3 + color.G * 0.59 + color.B * 0.11);
                    Color newColor = Color.FromArgb(gray, gray, gray);
                    bmp.SetPixel(i, j, newColor);
                }
            }

            return bmp;
        }

        /// <summary>
        /// 图像灰度反转
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static Bitmap GrayReverse(Bitmap bmp)
        {
            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    //获取该点的像素的RGB的颜色
                    Color color = bmp.GetPixel(i, j);
                    Color newColor = Color.FromArgb(255 - color.R, 255 - color.G, 255 - color.B);
                    bmp.SetPixel(i, j, newColor);
                }
            }

            return bmp;
        }

        /// <summary>
        /// 图像二值化1：取图片的平均灰度作为阈值，低于该值的全都为0，高于该值的全都为255
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static Bitmap ConvertTo1Bpp1(Bitmap bmp)
        {
            int average = 0;
            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    Color color = bmp.GetPixel(i, j);
                    average += color.B;
                }
            }

            average = (int) average / (bmp.Width * bmp.Height);

            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    //获取该点的像素的RGB的颜色
                    Color color = bmp.GetPixel(i, j);
                    int value = color.B;
                    Color newColor = value > average ? Color.FromArgb(255, 255, 255) : Color.FromArgb(0, 0, 0);
                    bmp.SetPixel(i, j, newColor);
                }
            }

            return bmp;
        }

        /// <summary>
        /// 图像二值化2
        /// </summary>
        /// <param name="img"></param>
        /// <returns></returns>
        public static Bitmap ConvertTo1Bpp2(Bitmap img)
        {
            int w = img.Width;
            int h = img.Height;
            Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
            BitmapData data = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite,
                PixelFormat.Format1bppIndexed);
            for (int y = 0; y < h; y++)
            {
                byte[] scan = new byte[(w + 7) / 8];
                for (int x = 0; x < w; x++)
                {
                    Color c = img.GetPixel(x, y);
                    if (c.GetBrightness() >= 0.5) scan[x / 8] |= (byte) (0x80 >> (x % 8));
                }

                Marshal.Copy(scan, 0, (IntPtr) ((int) data.Scan0 + data.Stride * y), scan.Length);
            }

            return bmp;
        }

        /// <summary>
        /// 图像二值化1：取图片的平均灰度作为阈值，低于该值的全都为0，高于该值的全都为255
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static bool CompImg(Bitmap bmpori, Bitmap bitmapCompare)
        {
            var icountsuc = 0;
            var icountfail = 0;

            for (int i = 0; i < bmpori.Width; i++)
            {
                for (int j = 0; j < bmpori.Height; j++)
                {
                    var colorori = bmpori.GetPixel(i, j);
                    var colornew = bitmapCompare.GetPixel(i, j);
                    if (colorori.B == colornew.B)
                    {
                        icountsuc++;
                    }
                    else
                    {
                        icountfail++;
                    }
                }
            }

            var itotal = icountsuc + icountfail;
            if (icountsuc > itotal * 0.9)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 图片缩放
        /// </summary>
        /// <param name="b"></param>
        /// <param name="destHeight"></param>
        /// <param name="destWidth"></param>
        /// <returns></returns>
        public static Bitmap GetThumbnail(Bitmap b, int destHeight, int destWidth)
        {
            System.Drawing.Image imgSource = b;
            System.Drawing.Imaging.ImageFormat thisFormat = imgSource.RawFormat;
            int sW = 0, sH = 0;
            // 按比例缩放           
            int sWidth = imgSource.Width;
            int sHeight = imgSource.Height;
            if (sHeight > destHeight || sWidth > destWidth)
            {
                if ((sWidth * destHeight) > (sHeight * destWidth))
                {
                    sW = destWidth;
                    sH = (destWidth * sHeight) / sWidth;
                }
                else
                {
                    sH = destHeight;
                    sW = (sWidth * destHeight) / sHeight;
                }
            }
            else
            {
                sW = sWidth;
                sH = sHeight;
            }

            Bitmap outBmp = new Bitmap(destWidth, destHeight);
            Graphics g = Graphics.FromImage(outBmp);
            g.Clear(Color.Transparent);
            // 设置画布的描绘质量         
            g.CompositingQuality = CompositingQuality.HighQuality;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(imgSource, new Rectangle((destWidth - sW) / 2, (destHeight - sH) / 2, sW, sH), 0, 0,
                imgSource.Width, imgSource.Height, GraphicsUnit.Pixel);
            g.Dispose();
            // 以下代码为保存图片时，设置压缩质量     
            EncoderParameters encoderParams = new EncoderParameters();
            long[] quality = new long[1];
            quality[0] = 100;
            EncoderParameter encoderParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, quality);
            encoderParams.Param[0] = encoderParam;
            imgSource.Dispose();
            return outBmp;
        }


        /// <summary>
        /// 图片识别
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        public static string Reconize(OcrType ocrtype, Bitmap bmp)
        {
            var url =
                $"{ServerConfig.ServerAddress}/api/oms/ReconizePng/Proc?uploadfile=1&token={ServerConfig.GetToken()}&OcrType={ocrtype.ToString()}";
            System.IO.MemoryStream memory = new System.IO.MemoryStream();
            bmp.Save(memory, System.Drawing.Imaging.ImageFormat.Jpeg);
            var ss = Common.handleHttp.FileUpload(memory.ToArray(), "capture", "", url);
            var result = Common.handleJSON.DeserializeResult(ss);
            if (result.result == 1)
            {
                return result.GetVal("text").ToString();
            }
            else
            {
                return "";
            }
        }

        #region [Baidu-Paddle图片文字识别-chinese_ocr_db_crnn_server]

        public static ReconizePaddleReturn.ValuePaddle ReconizePaddle(Bitmap bitmap)
        {
            #region Bitamp ConvertTo Base64

            // Convert byte[] to Base64 String
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, ImageFormat.Png);
            byte[] byteImage = ms.ToArray();
            var SigBase64 = Convert.ToBase64String(byteImage); // Get Base64

            #endregion

            var bodys = $"'images':['{SigBase64}']";
            bodys = "{" + bodys + "}";
            var jsbodys = JObject.Parse(bodys);
            var _urlocr = " http://47.106.107.225:7890/predict/chinese_ocr_db_crnn_server";
            HttpWebRequest httpRequest = null;
            HttpWebResponse httpResponse = null;

            if (_urlocr.Contains("https://"))
            {
                ServicePointManager.ServerCertificateValidationCallback =
                    new RemoteCertificateValidationCallback(CheckValidationResult);
                httpRequest = (HttpWebRequest)WebRequest.CreateDefault(new Uri(_urlocr));
            }
            else
            {
                httpRequest = (HttpWebRequest)WebRequest.Create(_urlocr);
            }

            httpRequest.Method = WebRequestMethods.Http.Post;
            httpRequest.Headers.Add("Content-type", "application/json");
            httpRequest.ContentType = "application/json; charset=UTF-8";
            httpRequest.Timeout = 6000;
            if (0 < bodys.Length)
            {
                byte[] data = Encoding.UTF8.GetBytes(jsbodys.ToString());
                using (Stream stream = httpRequest.GetRequestStream())
                {
                    httpRequest.ContentLength = data.Length;
                    stream.Write(data, 0, data.Length);
                    stream.Close();
                }
            }

            try
            {
                httpResponse = (HttpWebResponse)httpRequest.GetResponse();
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0}", ex.Message);
                throw;
            }

            Stream st = httpResponse.GetResponseStream();
            StreamReader reader = new StreamReader(st, Encoding.GetEncoding("utf-8"));
            var result = reader.ReadToEnd();
            var reRturn = JsonConvert.DeserializeObject<ReconizePaddleReturn.Root>(result);
            var l_datas = reRturn.results;
            var valuePaddle = new ReconizePaddleReturn.ValuePaddle();
            foreach (var datas in l_datas)
            {
                var l_data = datas.data;
                foreach (var data in l_data)
                {
                    var match = Regex.Match(data.text, "\\d+");
                    valuePaddle.text = match.Value;
                    valuePaddle.confidence = data.confidence;
                }
            }
            return valuePaddle;
        }
        
        public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain,
            SslPolicyErrors errors)
        {
            return true;
        }

        #endregion
        

        /// <summary>
        /// 放大图片
        /// </summary>
        /// <param name="bmp"></param>
        /// <returns></returns>
        public static Bitmap Large(Bitmap bmpori)
        {
            Bitmap bmpnew = new Bitmap(bmpori.Width * 2, bmpori.Height * 2);

            for (int i = 1; i < bmpori.Width - 1; i++)
            {
                for (int j = 1; j < bmpori.Height - 1; j++)
                {
                    var colorori = bmpori.GetPixel(i, j);
                    int gray = (int) (colorori.R * 0.3 + colorori.G * 0.59 + colorori.B * 0.11);
                    Color newColor = Color.FromArgb(gray, gray, gray);
                    //var colorori = bmpori.GetPixel(i, j);
                    var xa = i * 2 - 1;
                    var xb = i * 2;
                    var ya = j * 2 - 1;
                    var yb = j * 2;
                    bmpnew.SetPixel(xa, ya, newColor);
                    bmpnew.SetPixel(xb, ya, newColor);
                    bmpnew.SetPixel(xa, yb, newColor);
                    bmpnew.SetPixel(xb, yb, newColor);
                }
            }

            return bmpnew;
        }

        //图片处理
        public static Bitmap PicProcess(Bitmap procBmp)
        {
            Mat srcMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(procBmp);
            var src = new Mat();
            Cv2.CvtColor(srcMat, src, ColorConversionCodes.RGB2GRAY); //灰度化
            Cv2.Resize(src, src, new OpenCvSharp.Size(4 * src.Width, 4 * src.Height), 0, 0,
                InterpolationFlags.Cubic); //图片缩放，目前两倍
            Mat binary = new Mat(); //二值化
            Mat dst = new Mat();
            Cv2.Threshold(src, binary, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
            // Cv2.Normalize(src,dst,alpha:0,beta:255,normType: NormTypes.MinMax);
            // Cv2.Threshold(dst, binary, 0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
            // var mask = new Mat();
            // Cv2.FloodFill(binary, mask,new OpenCvSharp.Point(0,0), 0);
            // Cv2.FloodFill(binary, mask,new OpenCvSharp.Point(0,0), 255);
            var binarybmp = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(binary);
            binarybmp = fastNlMeansDenoising(binarybmp);    //去除噪点
            binarybmp = addWeighted(binarybmp);     //图片叠加
            var outbinary = MedianFilter(binarybmp); //图片平滑处理
            return outbinary;
        }

        #region 定位样本位置
        //定位样本位置
        public static System.Drawing.Point
            FindPicFromImage(Bitmap imgSrc, Bitmap imgSub, double threshold = 0.9) //Template Matching，threshold默认0.9
        {
            Mat srcMat = null;
            Mat dstMat = null;
            OutputArray outArray = null;
            try
            {
                srcMat = OpenCvSharp.Extensions.BitmapConverter
                    .ToMat(imgSrc); //conversion between System.Drawing.Bitmap and Mat
                dstMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(imgSub);
                outArray = OutputArray.Create(srcMat); //Creates a proxy class of the specified matrix
                Cv2.MatchTemplate(srcMat, dstMat, outArray, TemplateMatchModes.CCoeff); //Template Matching
                double minValue, maxValue;
                OpenCvSharp.Point location, point;
                Cv2.MinMaxLoc(InputArray.Create(outArray.GetMat()), out minValue, out maxValue,
                    out location, out point);
                if (maxValue >= threshold) //图片相似度
                    return new System.Drawing.Point(point.X, point.Y);
                return System.Drawing.Point.Empty;
            }
            catch
            {
                return System.Drawing.Point.Empty;
            }
            finally
            {
                if (srcMat != null)
                    srcMat.Dispose();
                if (dstMat != null)
                    dstMat.Dispose();
                if (outArray != null)
                    outArray.Dispose();
            }
        }

        #endregion

        #region 灰度处理

        public static Bitmap Grayscale(Bitmap procBmp)
        {
            Mat srcMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(procBmp);
            Mat outMat = new Mat();
            Cv2.CvtColor(srcMat, outMat, ColorConversionCodes.BGRA2GRAY);
            Bitmap outBmp = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outMat);
            return outBmp;
        }

        #endregion

        #region SIFT 相似度比较
        //图片相似度比较，返回图片相似度：0.0~1.0
        public static double MatchPicBySift(Bitmap srcBmp, Bitmap subBmp)
        {
            srcBmp = Grayscale(srcBmp); //灰度化
            subBmp = Grayscale(subBmp);
            Mat srcMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(srcBmp);
            Mat subMat = OpenCvSharp.Extensions.BitmapConverter.ToMat(subBmp);
            Mat srcMatRet = new Mat();
            Mat subMatRet = new Mat();
            KeyPoint[] srckeyPoints, subKeyPoints;
            var sift = OpenCvSharp.XFeatures2D.SIFT.Create();
            sift.DetectAndCompute(srcMat, null, out srckeyPoints, srcMatRet);
            sift.DetectAndCompute(subMat, null, out subKeyPoints, subMatRet);
            var bfMatcher = new OpenCvSharp.BFMatcher();
            var matches = bfMatcher.KnnMatch(srcMatRet, subMatRet, k: 2);
            double maxDistance = 0;
            foreach (var items in matches)
            {
                if (items[1].Distance == 0.0)
                {
                    return 0.0;
                }
                maxDistance = Math.Max(maxDistance, items[1].Distance);
            }

            var goodMatches = new List<DMatch>();
            foreach (var items in matches)
            {
                if (items[0].Distance < 0.5 * maxDistance)
                {
                    goodMatches.Add(items[0]);
                }
            }

            if (matches.Count() == 0)
            {
                return 0.0;
            }

            return Math.Round(Math.Pow((double) goodMatches.Count() / matches.Count(), 1.0 / 4), 2);
        }

        #endregion

        #region [中值滤波]

        //中值滤波
        public static Bitmap MedianFilter(Bitmap srcbmp)
        {
            Mat srcmat = BitmapConverter.ToMat(srcbmp);
            Mat outmat = new Mat();
            Cv2.MedianBlur(srcmat, outmat, 3);
            var outbmp = BitmapConverter.ToBitmap(outmat);
            return outbmp;
        }

        #endregion

        #region [图片降噪处理]

        //降噪
        public static Bitmap fastNlMeansDenoising(Bitmap srcbmp)
        {
            Mat srcmat = OpenCvSharp.Extensions.BitmapConverter.ToMat(srcbmp);
            Mat outmat = new Mat();
            Cv2.FastNlMeansDenoising(srcmat, outmat,20);
            var outbmp = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outmat);
            return outbmp;
        }
        
        //图片重叠
        public static Bitmap addWeighted(Bitmap srcbmp)
        {
            Mat srcmat = OpenCvSharp.Extensions.BitmapConverter.ToMat(srcbmp);
            Mat outmat = new Mat();
            Cv2.AddWeighted(srcmat,1, srcmat,1,0,outmat);
            Cv2.AddWeighted(outmat,1, srcmat,1,0,outmat);
            Cv2.AddWeighted(outmat,1, srcmat,1,0,outmat);
            Cv2.AddWeighted(outmat,1, srcmat,1,0,outmat);
            Cv2.AddWeighted(outmat,1, srcmat,1,0,outmat);
            Cv2.AddWeighted(outmat,1, srcmat,1,0,outmat);
            Cv2.AddWeighted(outmat,1, srcmat,1,0,outmat);
            var outbmp = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(outmat);
            return outbmp;
        }
        
        

        #endregion
    }
}